﻿using Common;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Cjk;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Analysis.TokenAttributes;
using System;
using System.Linq;
using System.Collections;
using System.IO;
using System.Collections.Generic;
using Lucene.Net.Store;
using Lucene.Net.Index;
using Lucene.Net.Analysis.En;
using Lucene.Net.Documents;
using Lucene.Net.Search;
using Lucene.Net.Util;
using LuceneDemo.Extension;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using LuceneDemo.Service;
using LuceneDemo.Interface;
using CCSoft.StorageRun.Infrastructure;
using System.Data;
using System.Text;

namespace LuceneDemo
{
    class Program
    {
        static IServiceProvider ServiceProvider { get; set; }
        static void Main(string[] args)
        {
            //Console.WriteLine("Hello World!");

            //StandardAnalyzer();
            //CJKAnalyzer();

            DecompressExcel();

            IServiceCollection container = new ServiceCollection();
            container.AddTransient<IKeywordLuceneService, KeywordLuceneService>();
            ServiceProvider = container.BuildServiceProvider();

            Test();
            //ParallelTest(10000);
            //SyncTest(10000);

            Console.ReadKey();
        }
        static void DecompressExcel()
        {
            var origExcelPath = @"C:\Users\PC\Downloads\20210520\cab-get email data of site 1000 in x 20210520.csv";
            var resultPath = @"C:\Users\PC\Downloads\20210520\decompress.csv";
            ITransferData transferData = new CsvTransferData();
            var origTable = transferData.GetData(origExcelPath);
            var resultTable = origTable.Clone();
            foreach(DataRow row in origTable.Rows)
            {
                var decompressRow = resultTable.NewRow();
                var body = Utils.ConvertToBytesFromDataBaseBinaryString(Convert.ToString(row[0])); //Encoding.UTF8.GetBytes(Convert.ToString(row[0]));
                var decompressBody = Utils.Decompress(body);
                decompressRow[0] = decompressBody;
                decompressRow[1] = row[1];
                decompressRow[2] = row[2];
                resultTable.Rows.Add(decompressRow);
            }
            transferData.SaveData(resultTable, resultPath);
        }
        static void SyncTest(int size)
        {
            for (var i = 0; i < size; i++)
            {
                Console.WriteLine($"test bot{i+1}");
                Test();
            }
        }
        static void ParallelTest(int size)
        {
            IList<Task> tasks = new List<Task>();
            for(var i=0; i<size; i++)
            {
                tasks.Add(new Task(() => Test()));
            }
            Parallel.ForEach(tasks, a => {
                a.Start();
            });
        }
        static void Test()
        {
            var bot = Bot();
            var input = new TrainInput
            {
                Bot = bot,
                Intents = bot.Category.SelectMany(a => a.Intent).Select(a => { a.Id = Guid.NewGuid(); return a; }),
            };

            System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
            stopwatch.Reset();
            stopwatch.Start();

            Train(input);

            stopwatch.Stop();

            Console.WriteLine($"Train time:{stopwatch.ElapsedMilliseconds}");

            stopwatch.Restart();

            var intent = Utils.GetRandomItemFromList(input.Intents.ToList());
            var question = intent.Question.FirstOrDefault().Content + " test";

            var intentscores = GetIntentByQuestion(new GetIntentByQuestionInput
            {
                Bot = bot,
                Question = question,
            }).Take(3).ToArray();

            Console.WriteLine($"GetIntentByQuestion time:{stopwatch.ElapsedMilliseconds}");

            Console.WriteLine("***************************");
            Console.WriteLine($"intent:{intent.IntentName},question:{question},查询结果数:{intentscores.Length}");
            Console.WriteLine("***************************");
            Array.ForEach(intentscores, a =>
            {
                Console.WriteLine($"{a.Id}|{a.Name}|{a.Score}");
            });
        }
        static BotType Bot()
        {
            var botImportData = LoadBotImportData();
            var bot = botImportData.Bot;
            bot.Id = Guid.NewGuid();//b020df4b-54ad-4808-a6db-9ce20e26b4cf
            bot.SiteId = 1000;
            return bot;
        }
        static IEnumerable<IntentScore> EnrichIntentScores(IEnumerable<IntentScore> intentScores)
        {
            if (intentScores != null)
            {
                return intentScores
                    .ResetScore(MaxScore(intentScores))
                    .Where(a => a.Id != Guid.Empty && a.Score > 0)
                    .GroupBy(a => a.Id)
                    .Select(a => a.OrderByDescending(a => a.Score).FirstOrDefault());
            }

            return null;
        }
        static IndexSearcher IndexSearcher(Lucene.Net.Store.Directory index)
        {
            IndexReader reader = DirectoryReader.Open(index);
            IndexSearcher searcher = new IndexSearcher(reader);
            return searcher;
        }
        static IEnumerable<ScoreDoc> ScoreDocs(Lucene.Net.Store.Directory index, IndexSearcher searcher, string question)
        {
            var stopWords = EnglishAnalyzer.DefaultStopSet;
            var analyzer = new EnglishAnalyzer(Lucene.Net.Util.LuceneVersion.LUCENE_48, stopWords);
            QueryBuilder builder = new QueryBuilder(analyzer);
            Query query = builder.CreateBooleanQuery("question", question);

            var collector = TopScoreDocCollector.Create(100, true);
            searcher.Search(query, collector);
            var hits = collector.GetTopDocs().ScoreDocs;
            return hits;
        }
        static IEnumerable<IntentScore> ParseIntentScoreCollection(IndexSearcher searcher, IEnumerable<ScoreDoc> scoreDocs)
        {
            if (scoreDocs != null)
            {
                IList<IntentScore> result = new List<IntentScore>();
                foreach (var hit in scoreDocs)
                {
                    var document = searcher.Doc(hit.Doc);
                    result.Add(new IntentScore
                    {
                        Id = Guid.Parse(document.Get("intentId")),
                        Name = document.Get("intentName"),
                        Score = hit.Score,
                    });
                }
                return result;
            }

            return null;
        }
        static float MaxScore(IEnumerable<IntentScore> intentScores)
        {
            return intentScores.FirstOrDefault(f => f.Id == Guid.Empty).Score;
        }
        static void IndexAddQuestion(BotType bot, Lucene.Net.Store.Directory index, string question)
        {
            using (IndexWriter writer = IndexWriter(bot, index))
            {
                var intent = new BotIntentType
                {
                    Id = Guid.Empty,
                    IntentName = string.Empty,

                };
                var botIntentQuestionType = new BotIntentQuestionType
                {
                    Content = question,
                };
                Document document = IntentQuestionMapDocument(intent, botIntentQuestionType);
                writer.UpdateDocument(new Term("intentid", Guid.Empty.ToString()), document);
                writer.Commit();
            }
        }
        static void IndexAddDocuments(Lucene.Net.Store.Directory index, TrainInput input)
        {
            using (IndexWriter writer = IndexWriter(input.Bot, index))
            {
                foreach (var intent in input.Intents)
                {
                    foreach (var question in intent.Question)
                    {
                        Document document = IntentQuestionMapDocument(intent, question);
                        writer.AddDocument(document);
                    }
                }
                writer.Commit();
            }
        }
        static void RemoveIndex(BotType bot)
        {
            //var path = Path(bot);
            //CreateDirectory(path);

            //FSDirectory index = FSDirectory.Open(path);
            //if (index == null)
            //{
            //    index = new SimpleFSDirectory(path);
            //}
            var key = Path(bot);
            Utils.RemoveMemoryCache(key);
        }
        static Lucene.Net.Store.Directory Index(BotType bot)
        {
            //var path = Path(bot);
            //CreateDirectory(path);

            //FSDirectory index = FSDirectory.Open(path);
            //if (index == null)
            //{
            //    index = new SimpleFSDirectory(path);
            //}
            var key = Path(bot);
            var index = Utils.GetMemoryCache(key, ()=> new RAMDirectory(), 0);
            return index; 
        }
        static IndexWriter IndexWriter(BotType bot, Lucene.Net.Store.Directory index)
        {
            var stopWords = EnglishAnalyzer.DefaultStopSet;
            var analyzer = new EnglishAnalyzer(Lucene.Net.Util.LuceneVersion.LUCENE_48, stopWords);
            var config = new IndexWriterConfig(Lucene.Net.Util.LuceneVersion.LUCENE_48, analyzer);
            IndexWriter writer = new IndexWriter(index, config);
            return writer;
        }
        static Document IntentQuestionMapDocument(BotIntentType intent, BotIntentQuestionType question)
        {
            Document document = new Document();
            document.Add(new StoredField("intentId", intent.Id.ToString()));
            document.Add(new StoredField("intentName", intent.IntentName));
            document.Add(new TextField("question", new StringReader(question.Content)));
            return document;
        }
        static void CreateDirectory(string path)
        {
            var lastPosition = path.LastIndexOf('/');
            var dir = path.Substring(0, lastPosition);
            if (!System.IO.Directory.Exists(dir))
            {
                System.IO.Directory.CreateDirectory(dir);
            }
        }
        static string Path(BotType bot)
        {
            var baseDir = "index";
            var path = $"{baseDir}/{bot.SiteId}/{bot.Id}";
            return path;
        }
        static IEnumerable<IntentScore> GetIntentByQuestion(GetIntentByQuestionInput input)
        {
            //var path = Path(input.Bot);

            //Lucene.Net.Store.Directory index = Index(input.Bot);//path = baseDir + siteId + botId。
            //IndexAddQuestion(input.Bot, index, input.Question);
            //IndexSearcher searcher = IndexSearcher(index);
            //var hits = ScoreDocs(index, searcher, input.Question);
            //var result = ParseIntentScoreCollection(searcher, hits);
            //var enrich = EnrichIntentScores(result);
            //return enrich;

            var keywordLuceneService = ServiceProvider.GetService<IKeywordLuceneService>();
            var result = keywordLuceneService.Search(input);
            return result;
        }
        static void Train(TrainInput input)
        {
            if(input!=null
                && input.Bot != null
                && input.Intents != null)
            {
                //RemoveIndex(input.Bot);
                //Lucene.Net.Store.Directory index = Index(input.Bot);
                //IndexAddDocuments(index, input);

                var keywordLuceneService = ServiceProvider.GetService<IKeywordLuceneService>();
                keywordLuceneService.IndexAddDocuments(input);
            }
        }
        static BotImportData LoadBotImportData()
        {
            var xml = Utils.GetFileContent("bot.xml");
            var botImportData = Utils.XmlDeserialize<BotImportData>(xml);
            return botImportData;
        }
        static void StandardAnalyzer()
        {
            Console.WriteLine("*************一元分词**************");
            Analyzer standardAnalyzer = new StandardAnalyzer(Lucene.Net.Util.LuceneVersion.LUCENE_48);
            TokenStream standardAnalyzerTokenStream = standardAnalyzer.GetTokenStream("", new StringReader("我爱你中国China中华人名共和国"));
            ICharTermAttribute standardAnalyzerTerm = standardAnalyzerTokenStream.GetAttribute<ICharTermAttribute>();
            standardAnalyzerTokenStream.Reset();
            while (standardAnalyzerTokenStream.IncrementToken())
            {
                Console.WriteLine(standardAnalyzerTerm.ToString());
            }
        }
        static void CJKAnalyzer()
        {
            Console.WriteLine("*************二元分词**************");
            Analyzer cjkAnalyzer = new CJKAnalyzer(Lucene.Net.Util.LuceneVersion.LUCENE_48);
            TokenStream tokenStream = cjkAnalyzer.GetTokenStream("", new StringReader("我爱你中国China中华人名共和国"));
            ICharTermAttribute term = tokenStream.GetAttribute<ICharTermAttribute>();
            tokenStream.Reset();
            while (tokenStream.IncrementToken())
            {
                Console.WriteLine(term.ToString());
            }
        }
    }
}
